// 导入相关依赖包，包括阿里巴巴Nacos的工具类、自定义配置属性XssProperties、自定义包装类XssHttpServletRequestWrapper，以及Servlet和Spring框架的相关注解和接口

package com.satan.novel.config.filter;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.satan.novel.config.properties.XssProperties;
import com.satan.novel.config.wrapper.XssHttpServletRequestWrapper;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: Demon
 * @Date: 2024/1/20 19:48
 * @Description: 防止 XSS 攻击的过滤器，用于对HTTP请求参数进行过滤处理，移除潜在的跨站脚本攻击（XSS）威胁
 **/
@Component // 将该过滤器作为Spring Bean管理
@ConditionalOnProperty(value = "novel.xss.enabled", havingValue = "true") // 当novel.xss.enabled属性值为true时启用此过滤器
@WebFilter(urlPatterns = "/*", filterName = "xssFilter") // 注册一个全局过滤器，拦截所有请求
@EnableConfigurationProperties(value = {XssProperties.class}) // 启用XssProperties配置属性类
@RequiredArgsConstructor // Lombok注解，生成无参构造器并注入final修饰的字段
public class XssFilter implements Filter {
    // 注入XssProperties配置类实例，用于获取需要排除XSS过滤的URL模式列表
    private final XssProperties xssProperties;

    /**
     * 初始化方法，由容器调用
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig); // 调用父类初始化方法
    }

    /**
     * 实现核心过滤逻辑，对每个请求执行XSS过滤
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest; // 强制转换为HttpServletRequest类型

        // 判断当前请求是否需要从XSS过滤中排除
        if (handleExcludeUrl(req)) {
            filterChain.doFilter(servletRequest, servletResponse); // 如果需要排除，则直接放行
            return;
        }

        // 对请求进行XSS过滤
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest);
        filterChain.doFilter(xssRequest, servletResponse); // 使用包装后的请求对象继续执行过滤链
    }

    /**
     * 判断给定的请求URL是否需要从XSS过滤中排除
     */
    private boolean handleExcludeUrl(HttpServletRequest request) {
        // 检查配置中是否有需要排除的URL模式列表，为空则直接返回false
        if (CollectionUtils.isEmpty(xssProperties.excludes())) {
            return false;
        }

        // 获取当前请求的Servlet路径
        String url = request.getServletPath();

        // 遍历所有排除URL模式
        for (String pattern : xssProperties.excludes()) {
            // 将排除模式转换为正则表达式，并以"^"开头匹配URL起始部分
            Pattern p = Pattern.compile("^" + pattern);

            // 创建Matcher对象尝试匹配当前请求的Servlet路径
            Matcher m = p.matcher(url);

            // 如果在URL中找到匹配项，则表示当前请求URL应被排除于XSS过滤之外，返回true
            if (m.find()) {
                return true;
            }
        }

        // 若遍历完所有模式后均未找到匹配项，则该请求URL不应被排除，返回false
        return false;
    }

    /**
     * 销毁方法，在过滤器生命周期结束时由容器调用
     */
    @Override
    public void destroy() {
        Filter.super.destroy(); // 调用父类销毁方法
    }
}
